﻿Imports System.Runtime.CompilerServices
Imports System.Reflection
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.IO
Imports System.Web

Public Module MysteryExtensions

    Private _mystery As New Mystery
    Private _mystery_type As Type = GetType(Mystery)


    <Extension()>
    Public Function getMystery(current As Object) As Mystery
        'makcing sure it is instanced
        Dim ar As AssemblyRegister = _mystery.AssemblyRegister
        Return _mystery
    End Function


    Private _overrides As New Dictionary(Of Type, Object)

    <Extension()>
    Public Function getGlobalObject(Of T)(current As Object) As T
        Dim type As Type = GetType(T)
        If type Is _mystery_type Then Throw New Exception("use getMystery")
        If _overrides.ContainsKey(type) Then Return _overrides(type)
        If Not _mystery.AssemblyRegister.hasArgumentLessRetriver(type) Then
            If type.GetCustomAttributes(GetType(GlobalAvalibleObject), False).FirstOrDefault IsNot Nothing OrElse
                type.GetCustomAttributes(GetType(GlobalAvalibleObjectImplementation), False).FirstOrDefault IsNot Nothing Then
                _mystery.AssemblyRegister.Register(type.Assembly)
            Else
                Throw New Exception(type.FullName & " does seems to be a valid global type")
            End If
        End If
        Dim retriver As Func(Of Object) = _mystery.AssemblyRegister.getArgumentLessRetriver(type)
        If retriver IsNot Nothing Then
            Return retriver()
        Else
            Return Nothing
        End If

    End Function

    <Extension()>
    Public Function setGlobalObject(Of T)(current As Object, new_instance As T) As T
        _overrides(GetType(T)) = new_instance
    End Function


    <Extension()>
    Public Function getFirstMethodWithAttribute(Of T As Attribute)(current As Type) As MethodInfo
        Dim mi As MethodInfo = (From x As MethodInfo In current.GetMethods() Select x
                                Where x.GetCustomAttributes(GetType(T), True).FirstOrDefault IsNot Nothing).FirstOrDefault
        Return mi
    End Function

    <Extension()>
    Public Function getFirstAttribute(Of T As MysteryPropertyAttribute)(current As Type) As T
        Return _mystery.AssemblyRegister.getMysteryPropertyAttributes(Of T)(current).FirstOrDefault
    End Function

    <Extension()>
    Public Function getMysteryAttribute(Of T As MysteryClassAttribute)(type As Type) As T
        Return _mystery.AssemblyRegister.getMysteryClassAttribute(Of T)(type)
    End Function

    <Extension()>
    Public Function sameContents(base As IEnumerable(Of IContent), other As IEnumerable(Of IContent)) As Boolean
        If base Is Nothing AndAlso other Is Nothing Then Return True
        If base Is Nothing Then Return False
        If other Is Nothing Then Return False
        Dim base_hash As New HashSet(Of IContent)(base)
        Dim other_hash As New HashSet(Of IContent)(other)
        If base_hash.Count <> other_hash.Count Then Return False
        Return base_hash.Intersect(other_hash).Count = base_hash.Count
    End Function

    <Extension()>
    Public Function getMysteryAttribute(Of T As MysteryPropertyAttribute)(property_info As PropertyInfo) As T
        Return _mystery.AssemblyRegister.getMysteryPropertyAttributes(Of T)(property_info).FirstOrDefault
    End Function

    <Extension()>
    Public Sub addRange(Of T)(start_coll As ICollection(Of T), to_add As IEnumerable(Of T))
        If to_add Is Nothing Then Exit Sub
        For Each item As T In to_add
            start_coll.Add(item)
        Next
    End Sub

    ''' <summary>
    ''' take all the property with the same name and type
    ''' </summary>
    ''' <param name="to_content">content target</param>
    ''' <param name="from_content">content source</param>
    ''' <remarks></remarks>
    <Extension()>
    Public Sub copyFrom(to_content As IContent, from_content As IContent)
        If to_content Is Nothing OrElse from_content Is Nothing Then Exit Sub
        Dim target_type As Type = to_content.GetType
        Dim source_type As Type = from_content.GetType

        Dim all As IEnumerable(Of ContentProperty) = getMystery(Nothing).AssemblyRegister.getMysteryPropertyAttributes(Of ContentProperty)(source_type)
        Dim source_properties As New HashSet(Of ContentProperty)((From x As ContentProperty In all Select x Where x.retrive IsNot Nothing))
        Dim references As IEnumerable(Of ContentReference) = getMystery(Nothing).AssemblyRegister.getMysteryPropertyAttributes(Of ContentReference)(source_type)
        Dim source_reference As New HashSet(Of ContentReference)((From x As ContentReference In references Select x Where x.retrive IsNot Nothing))

        all = getMystery(Nothing).AssemblyRegister.getMysteryPropertyAttributes(Of ContentProperty)(target_type)
        Dim target_properties As New HashSet(Of ContentProperty)((From x As ContentProperty In all Select x Where x.retrive IsNot Nothing))
        references = getMystery(Nothing).AssemblyRegister.getMysteryPropertyAttributes(Of ContentReference)(target_type)
        Dim target_reference As New HashSet(Of ContentReference)((From x As ContentReference In references Select x Where x.retrive IsNot Nothing))

        Dim source_map As New Dictionary(Of String, MysteryPropertyAttribute)
        For Each sp As ContentProperty In source_properties
            source_map(sp.name) = sp
        Next
        For Each sp As ContentReference In source_reference
            source_map(sp.name) = sp
        Next

        Dim target_map As New Dictionary(Of String, MysteryPropertyAttribute)
        For Each tp As ContentProperty In target_properties
            target_map(tp.name) = tp
        Next
        For Each tp As ContentReference In target_reference
            target_map(tp.name) = tp
        Next

        For Each pn As String In target_map.Keys
            Try
                If Not source_map.ContainsKey(pn) Then Continue For
                Dim source As MysteryPropertyAttribute = source_map(pn)
                Dim target As MysteryPropertyAttribute = target_map(pn)
                If Not source.used_in.PropertyType Is target.used_in.PropertyType Then Continue For
                Dim source_value As ContentPropertyValue = source.retrive(from_content)
                Dim target_value As ContentPropertyValue = target.retrive(to_content)
                target_value.setValue(source_value.getValue())
            Catch ex As Exception
                'well if some failed is not the end of the world, is it?
            End Try
        Next

    End Sub

    ''' <summary>
    ''' convert bytes into string
    ''' </summary>
    ''' <param name="bytes"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Extension()>
    Public Function getString(bytes As Byte()) As String
        Return System.Text.Encoding.UTF8.GetString(bytes)
    End Function

    ''' <summary>
    ''' conver string into bytes
    ''' </summary>
    ''' <param name="input"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Extension()>
    Public Function getBytes(input As String) As Byte()
        If String.IsNullOrEmpty(input) Then
            Dim result() As Byte = {}
            Return result
        End If
        Return System.Text.Encoding.UTF8.GetBytes(input)
    End Function

    ''' <summary>
    ''' convert bytes into string
    ''' </summary>
    ''' <param name="bytes"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Extension()>
    Public Function getBase64(bytes As Byte()) As String
        Return System.Convert.ToBase64String(bytes)
    End Function

    ''' <summary>
    ''' conver string into bytes
    ''' </summary>
    ''' <param name="input"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Extension()>
    Public Function getBytesBase64(input As String) As Byte()
        Return System.Convert.FromBase64String(input)
    End Function

    ''' <summary>
    ''' conver string into html text
    ''' </summary>
    ''' <param name="input"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Extension()>
    Public Function getHtml(input As String) As String
        If String.IsNullOrEmpty(input) Then Return "&nbsp;"
        Return HttpUtility.HtmlEncode(input).Replace(vbNewLine, "<br/>").Replace("€", "&euro;")
    End Function


    <Extension()>
    Public Function getSerializatedBytes(o As Object) As Byte()
        'ok message verificated and descripted!
        Dim formatter As IFormatter = New BinaryFormatter()
        Dim MS As New MemoryStream()
        formatter.Serialize(MS, o)
        Dim result() As Byte = MS.ToArray
        MS.Dispose()
        Return result
    End Function

    <Extension()>
    Public Function getObjectDeserializated(bytes As Byte()) As Object
        'ok message verificated and descripted!
        Dim formatter As IFormatter = New BinaryFormatter()
        Dim MS As New MemoryStream(bytes)
        Dim result As Object = formatter.Deserialize(MS)
        MS.Dispose()
        Return result
    End Function

    ''' <summary>
    ''' it does not include hour and minutes
    ''' </summary>
    ''' <param name="the_date"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Extension()>
    Public Function getNiceString(the_date As Date) As String
        Return the_date.ToString("dd MMM yyyy")
    End Function

    ''' <summary>
    ''' return a nice way get the money
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Extension()>
    Public Function getNiceMoneyString(money As Double) As String
        Return money.getNicePrice.ToString("C2")
    End Function


    ''' <summary>
    ''' it does round cents down
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    <Extension()>
    Public Function getNicePrice(price As Double) As Double
        Return Math.Round(price, 2)
    End Function

End Module
